<template lang="pug">
    #scatter
</template>

<script>
import * as d3 from 'd3';
import sd from '@/mock/mds';
import { mapMutations } from 'vuex';

export default {
    data() {
        return {
            data: sd['RECORDS'],
            svg: null,
            width: 328,
            height: 215,
            padding: { left: 1, top: 1, right: 1, bottom: 1 },
            xScale: null,
            yScale: null,
            zoomXScale: null,
            zoomYScale: null,
            zoom: null,
            color: null
        };
    },
    methods: {
        ...mapMutations({
            setController: 'setSelectCtrl',
            brushArea: 'selectTests',
            scatterRange: 'scatterRange'
        }),
        initScatter() {
            this.svg = d3
                .select('#scatter')
                .append('svg')
                .attr('width', this.width)
                .attr('height', this.height);

            this.zoomXScale = this.xScale = d3
                .scaleLinear()
                .domain(
                    d3.extent(this.data, v => {
                        return v.x;
                    })
                )
                .range([
                    0,
                    this.width - this.padding.left - this.padding.right
                ]);

            this.zoomYScale = this.yScale = d3
                .scaleLinear()
                .domain(
                    d3.extent(this.data, v => {
                        return v.y;
                    })
                )
                .range([
                    this.height - this.padding.top - this.padding.bottom,
                    0
                ]);

            this.color = d3
                .scaleOrdinal()
                .domain([0, 1])
                .range(['#ffaf65', '#9cdae6']);

            this.zoom = d3.zoom().scaleExtent([1, 40]);
        },
        createScatter() {
            let xAxis;
            let yAxis;
            let view;
            let gX, gY, gP; // 表示x轴group,y轴group,点group

            this.initScatter();

            // 绘制视图区背景
            view = this.svg
                .append('rect')
                .attr('class', 'view')
                .attr('x', this.padding.left)
                .attr('y', this.padding.top)
                .attr(
                    'width',
                    this.width - this.padding.left - this.padding.right
                )
                .attr(
                    'height',
                    this.height - this.padding.top - this.padding.bottom
                );

            // 初始化坐标轴对象
            xAxis = d3
                .axisTop(this.xScale)
                .ticks(5)
                .tickSize(5, 5);

            yAxis = d3
                .axisRight(this.yScale)
                .ticks(5)
                .tickSize(5, 5);

            /** 绘制坐标轴 **/
            gX = this.svg
                .append('g')
                .attr(
                    'transform',
                    'translate(' +
                        this.padding.left +
                        ',' +
                        (this.height - this.padding.bottom) +
                        ')'
                )
                .attr('class', 'x axis')
                .call(xAxis);

            gY = this.svg
                .append('g')
                .attr(
                    'transform',
                    'translate(' +
                        this.padding.left +
                        ',' +
                        this.padding.top +
                        ')'
                )
                .attr('class', 'y axis')
                .call(yAxis);

            /** 绘制散点 **/
            // 散点区域
            gP = this.svg
                .append('g')
                .attr('class', 'renderG')
                .attr(
                    'transform',
                    'translate(' +
                        this.padding.left +
                        ',' +
                        this.padding.top +
                        ')'
                );

            // 散点
            gP
                .selectAll('circle')
                .data(this.data)
                .enter()
                .append('circle')
                .attr('cx', d => this.xScale(d.x))
                .attr('cy', d => this.yScale(d.y))
                .attr('r', 1)
                .attr('fill-opacity', 0.6)
                .attr('fill', d => this.color(d.label));

            /** 增加缩放功能 **/
            this.svg.call(
                this.zoom.on('zoom', () => {
                    view.attr('transform', d3.event.transform);
                    gP.attr('transform', d3.event.transform);
                    gX.call(
                        xAxis.scale(
                            (this.zoomXScale = d3.event.transform.rescaleX(
                                this.xScale
                            ))
                        )
                    );
                    gY.call(
                        yAxis.scale(
                            (this.zoomYScale = d3.event.transform.rescaleY(
                                this.yScale
                            ))
                        )
                    );
                })
            );
        },
        reset() {
            this.svg
                .transition()
                .duration(750)
                .call(this.zoom.transform, d3.zoomIdentity);
        },
        showAxis(state) {
            this.svg.selectAll('.axis').classed('hide', !state);
        },
        setBrush(state) {
            let brush;

            if (state) {
                this.svg
                    .append('g')
                    .attr(
                        'transform',
                        'translate(' +
                            this.padding.left +
                            ',' +
                            this.padding.top +
                            ')'
                    )
                    .attr('class', 'brush')
                    .call(
                        (brush = d3
                            .brush()
                            .extent([
                                [0, 0],
                                [
                                    this.width -
                                        this.padding.left -
                                        this.padding.right,
                                    this.height -
                                        this.padding.top -
                                        this.padding.bottom
                                ]
                            ])
                            .on('start brush', this.brushing)
                            .on('end', this.brushend))
                    );
            } else {
                this.svg.select('.brush').on('.brush', null);
                this.svg.select('.brush').remove();
            }
        },
        brushing() {
            // const extent = d3.event.selection;

            d3
                .select('g.renderG')
                .selectAll('circle')
                .classed('renderG--selected', d => {
                    const extent = d3.event.selection;
                    const pos = [this.zoomXScale(d.x), this.zoomYScale(d.y)];

                    return (
                        pos[0] >= extent[0][0] &&
                        pos[0] <= extent[1][0] &&
                        pos[1] >= extent[0][1] &&
                        pos[1] <= extent[1][1]
                    );
                });
        },
        brushend() {
            this.setController('scatter');
            this.scatterRange(d3.event.selection);
            this.brushArea({
                range: d3
                    .select('g.renderG')
                    .selectAll('circle.renderG--selected')
                    .data()
                    .map(d => ({ test_id: d.test_id, label: d.label }))
            });
        }
    },
    mounted() {
        this.createScatter();
    }
};
</script>

<style lang="less">
#scatter {
    width: 100%;
    height: 100%;
    .axis > path {
        display: none;
    }
    .axis.hide {
        visibility: hidden;
    }
    .view {
        stroke: #000;
        opacity: 0;
    }
    .renderG {
        &--selected {
            stroke: brown;
        }
    }
}
</style>
